package com.xudm.cdg.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import com.xudm.cdg.entity.cosmic.CosmicExcelModel;
import com.xudm.cdg.entity.cosmic.SequenceDescription;
import net.sourceforge.plantuml.*;

import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * PlantumlUtil
 *
 * @author xudm
 */
public class PlantumlUtil {
    public static void main(String[] args) {
        String umlSource = "@startuml\n" +
                "'https://plantuml.com/sequence-diagram\n" +
                "\n" +
                "'自动显示序号\n" +
                "autonumber\n" +
                "'设置最大消息长度 不支持中文换行\n" +
                "skinparam maxMessageSize 50\n" +
                "'设置字体对齐方式\n" +
                "skinparam sequenceMessageAlign left\n" +
                "participant 项目管理功能 as xmglgn145 #white\n" +
                "participant 项目管理服务 as xmglfw886 #white\n" +
                "participant 数据层 as database471 #white\n" +
                "activate xmglgn145 #f1f2f6\n" +
                "activate xmglfw886 #f1f2f6\n" +
                "activate database471 #f1f2f6\n" +
                "xmglgn145 -> xmglfw886 : 通用工单转派服务获取\\n通用转派条件参数\n" +
                "xmglfw886 -> database471 : 从持久化数据库中读取\\n数据判断该通用工单是\\n否重复流转到转派节点\\n\n" +
                "database471 --> xmglfw886 : 返回数据\n" +
                "xmglfw886 -> database471 : 从持久化数据库中读取\\n通用工单的基本信息\n" +
                "database471 --> xmglfw886 : 返回数据\n" +
                "xmglfw886 -> database471 : 创建通用工单的转派信\\n息并写入持久化数据库\\n\n" +
                "database471 --> xmglfw886 : 返回数据操作结果\n" +
                "xmglfw886 -> database471 : 从持久化数据库中读取\\n配置好的通用工单转派\\n节点流程信息\n" +
                "database471 --> xmglfw886 : 返回数据\n" +
                "xmglfw886 -> database471 : 创建通用工单流程执行\\n转派日志信息并写入持\\n久化数据库\n" +
                "database471 --> xmglfw886 : 返回数据操作结果\n" +
                "xmglfw886 -> database471 : 更新通用工单转派节点\\n信息并写入持久化数据\\n库\n" +
                "database471 --> xmglfw886 : 返回数据操作结果\n" +
                "xmglfw886 --> xmglgn145 : 通用工单处理转派服务\\n返回通用转派结果\n" +
                "deactivate xmglgn145 #f1f2f6\n" +
                "deactivate xmglfw886 #f1f2f6\n" +
                "deactivate database471 #f1f2f6\n" +
                "@enduml";
        SourceStringReader reader = new SourceStringReader(umlSource);
        try (OutputStream os = new FileOutputStream("D:\\DevelopSpace\\XW_Space\\workspace\\COSMIC\\template\\sequenceDiagram.png")) {
            reader.generateImage(os);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getTestSourceString() {
        String umlSource = "@startuml\n" +
                "'https://plantuml.com/sequence-diagram\n" +
                "\n" +
                "'自动显示序号\n" +
                "autonumber\n" +
                "'设置最大消息长度 不支持中文换行\n" +
                "skinparam maxMessageSize 50\n" +
                "'设置字体对齐方式\n" +
                "skinparam sequenceMessageAlign left\n" +
                "participant 项目管理功能 as xmglgn145 #white\n" +
                "participant 项目管理服务 as xmglfw886 #white\n" +
                "participant 数据层 as database471 #white\n" +
                "activate xmglgn145 #f1f2f6\n" +
                "activate xmglfw886 #f1f2f6\n" +
                "activate database471 #f1f2f6\n" +
                "xmglgn145 -> xmglfw886 : 通用工单转派服务获取\\n通用转派条件参数\n" +
                "xmglfw886 -> database471 : 从持久化数据库中读取\\n数据判断该通用工单是\\n否重复流转到转派节点\\n\n" +
                "database471 --> xmglfw886 : 返回数据\n" +
                "xmglfw886 -> database471 : 从持久化数据库中读取\\n通用工单的基本信息\n" +
                "database471 --> xmglfw886 : 返回数据\n" +
                "xmglfw886 -> database471 : 创建通用工单的转派信\\n息并写入持久化数据库\\n\n" +
                "database471 --> xmglfw886 : 返回数据操作结果\n" +
                "xmglfw886 -> database471 : 从持久化数据库中读取\\n配置好的通用工单转派\\n节点流程信息\n" +
                "database471 --> xmglfw886 : 返回数据\n" +
                "xmglfw886 -> database471 : 创建通用工单流程执行\\n转派日志信息并写入持\\n久化数据库\n" +
                "database471 --> xmglfw886 : 返回数据操作结果\n" +
                "xmglfw886 -> database471 : 更新通用工单转派节点\\n信息并写入持久化数据\\n库\n" +
                "database471 --> xmglfw886 : 返回数据操作结果\n" +
                "xmglfw886 --> xmglgn145 : 通用工单处理转派服务\\n返回通用转派结果\n" +
                "deactivate xmglgn145 #f1f2f6\n" +
                "deactivate xmglfw886 #f1f2f6\n" +
                "deactivate database471 #f1f2f6\n" +
                "@enduml";
        return umlSource;
    }


    /**
     * 构建UML源码和描述
     *
     * @param list 数据列表
     * @return UML源码和描述
     */
    public static Object[] buildUmlSourceAndDescription(List<CosmicExcelModel> list) {
        StringBuilder sb = new StringBuilder();
        Object[] objects = new Object[2];
        if (CollUtil.isEmpty(list)) {
            return objects;
        }
        List<SequenceDescription> sequenceDescriptions = new ArrayList<>();
        Map<String, String> participantMap = new LinkedHashMap<>();
        sb.append("@startuml\n");
        sb.append("'https://plantuml.com/sequence-diagram\n");
        sb.append("'隐藏底部生命线\n");
        sb.append("hide footbox \n");
        sb.append("\n");
        sb.append("'自动显示序号\n");
        sb.append("autonumber\n");
        sb.append("'设置最大消息长度 不支持中文换行\n");
        sb.append("skinparam maxMessageSize 50\n");
        sb.append("'设置字体对齐方式\n");
        sb.append("skinparam sequenceMessageAlign left\n");


        CosmicExcelModel cosmicExcelModel = list.get(0);
        String stratification = cosmicExcelModel.getStratification();
        String functionalUser = cosmicExcelModel.getFunctionalUser();
        //提取发起者 如果发起者中存在定时关键字 则 为定时任务
        String initiator = SequenceDescriptionUtil.extractInitiator(functionalUser);
        String receiver = SequenceDescriptionUtil.extractReceiver(functionalUser);
        String functionalProcessing = cosmicExcelModel.getFunctionalProcessing();
        String modelName = cosmicExcelModel.getModelName();
        //Map<String, List<CosmicExcelModel>> stratificationMap = list.stream().collect(Collectors.groupingBy(CosmicExcelModel::getStratification));
        //多维度判断是否是功能处理还是服务处理
        boolean isFunction = false;
        if (receiver.contains("功能") || functionalProcessing.contains("功能") || stratification.contains("功能")) {
            isFunction = true;
        }

        if (isFunction) {
            //功能处理
            participantMap.put("个人用户", "gryh" + RandomUtil.randomInt(100, 999));
            participantMap.put(stratification, getChineseInitials(modelName + "功能") + RandomUtil.randomInt(100, 999));
        } else {
            //服务处理
            //看服务中是否存在 ios 和 Android 不区分大小写
            if (functionalProcessing.toLowerCase().contains("ios") || functionalProcessing.toLowerCase().contains("android")) {
                if (StrUtil.isNotBlank(initiator) && initiator.contains("定时")) {
                    participantMap.put("定时任务调度", "dsrw" + RandomUtil.randomInt(100, 999));
                } else {
                    participantMap.put(modelName + "功能", getChineseInitials(modelName + "功能") + RandomUtil.randomInt(100, 999));
                }
                participantMap.put(stratification, getChineseInitials(stratification) + RandomUtil.randomInt(100, 999));
            } else {
                if (StrUtil.isNotBlank(initiator) && initiator.contains("定时")) {
                    participantMap.put("定时任务调度", "dsrw" + RandomUtil.randomInt(100, 999));
                } else {
                    participantMap.put(modelName + "功能", getChineseInitials(modelName + "功能") + RandomUtil.randomInt(100, 999));
                }
                participantMap.put(stratification, getChineseInitials(stratification) + RandomUtil.randomInt(100, 999));
                participantMap.put("数据层", "database" + RandomUtil.randomInt(100, 999));
            }
        }

//        if (stratificationMap.size() == 1) {
//            stratificationMap.keySet().forEach(stratificationAtomic::set);
//            String stratification = stratificationAtomic.get();
//            if (stratification.endsWith("功能")) {
//                //功能处理
//                participantMap.put("个人用户", "gryh" + RandomUtil.randomInt(100, 999));
//                participantMap.put(stratification, getChineseInitials(stratification) + RandomUtil.randomInt(100, 999));
//            } else {
//                //服务处理
//                String modelName = list.get(0).getModelName();
//                participantMap.put(modelName + "功能", getChineseInitials(modelName + "功能") + RandomUtil.randomInt(100, 999));
//                participantMap.put(stratification, getChineseInitials(stratification) + RandomUtil.randomInt(100, 999));
//                participantMap.put("数据层", "database" + RandomUtil.randomInt(100, 999));
//            }
//        } else {
//            //多个分层
//            List<String> collect = stratificationMap.keySet().stream().filter(k -> !k.contains("数据")).collect(Collectors.toList());
//
//            String stratification = collect.get(0);
//            if (stratification.endsWith("功能")) {
//                //功能处理
//                participantMap.put("个人用户", "gryh" + RandomUtil.randomInt(100, 999));
//                participantMap.put(stratification, getChineseInitials(stratification) + RandomUtil.randomInt(100, 999));
//            } else {
//                //服务处理
//                String modelName = list.get(0).getModelName();
//                participantMap.put(modelName + "功能", getChineseInitials(modelName + "功能") + RandomUtil.randomInt(100, 999));
//                participantMap.put(stratification, getChineseInitials(stratification) + RandomUtil.randomInt(100, 999));
//                participantMap.put("数据层", "database" + RandomUtil.randomInt(100, 999));
//            }
//        }

        //声明参与者
        participantMap.keySet().forEach(k -> {
            sb.append("participant ").append(k).append(" as ").append(participantMap.get(k)).append(" #white\n");
        });

        //激活参与者
        participantMap.keySet().forEach(k -> {
            sb.append("activate ").append(participantMap.get(k)).append(" #f1f2f6\n");
        });

        //处理消息
        SequenceDescription sequenceDescription = new SequenceDescription();

//        String stratification = cosmicExcelModel.getStratification();
        String message = cosmicExcelModel.getSubFunctionalProcessing();

        //处理 E
        //默认是服务处理
        String fromOrigin = modelName + "功能";
        if (StrUtil.isNotBlank(initiator) && initiator.contains("定时")) {
            fromOrigin = "定时任务调度";
        }
        String toOrigin = stratification;
        int type = 2;
        String from = participantMap.get(fromOrigin);
        String to = participantMap.get(toOrigin);
        if (isFunction) {
            from = participantMap.get("个人用户");
            to = participantMap.get(stratification);
            fromOrigin = "个人用户";
            toOrigin = stratification;
            type = 1;
        }
        sequenceDescription.setType(type);
        sequenceDescription.setFrom(fromOrigin);
        sequenceDescription.setTo(toOrigin);
        //message 每10个字符增加一个换行
        message = lineBreak(message, 12);
        sb.append(from).append(" -> ").append(to).append(" : ").append(message).append("\n");
        //清除换行符
        String replace = message.replace("\\n", "");
        sequenceDescription.setMessage(replace);

        sequenceDescription.setDataGroup(cosmicExcelModel.getDataGroup());
        sequenceDescriptions.add(sequenceDescription);

        //循环处理读写子功能处理 R W
        String finalToOrigin = toOrigin;
        list.stream().filter(e -> !"E".equalsIgnoreCase(e.getDataMovementType()) && !"X".equalsIgnoreCase(e.getDataMovementType())).forEach(e -> {

            String subFunctionalProcessing = e.getSubFunctionalProcessing();
            String dataMovementType = e.getDataMovementType();
            //subFunctionalProcessing 每10个字符增加一个换行
            subFunctionalProcessing = lineBreak(subFunctionalProcessing, 10);
            //如果是查询数据 则 数据层 -> 功能处理
            if ("R".equalsIgnoreCase(dataMovementType)) {
                //数据层
                String from1 = participantMap.get(finalToOrigin);
                String to1 = participantMap.get("数据层");
                sb.append(from1).append(" -> ").append(to1).append(" : ").append(subFunctionalProcessing).append("\n");
                sb.append(to1).append(" --> ").append(from1).append(" : ").append("返回数据查询结果").append("\n");
                SequenceDescription sd1 = new SequenceDescription();
                sd1.setFrom(finalToOrigin);
                sd1.setTo("数据层");
                sd1.setMessage(subFunctionalProcessing.replace("\\n", ""));
                sd1.setDataGroup(e.getDataGroup());
                sd1.setType(2);
                sequenceDescriptions.add(sd1);
                SequenceDescription sd2 = new SequenceDescription();
                sd2.setFrom("数据层");
                sd2.setTo(finalToOrigin);
                sd2.setMessage("返回数据查询结果");
                sd2.setDataGroup("数据查询结果");
                sd2.setType(2);
                sequenceDescriptions.add(sd2);
            } else {
                //数据层 --> 功能处理
                String from1 = participantMap.get(finalToOrigin);
                String to1 = participantMap.get("数据层");
                sb.append(from1).append(" -> ").append(to1).append(" : ").append(subFunctionalProcessing).append("\n");
                sb.append(to1).append(" --> ").append(from1).append(" : ").append("返回数据操作结果").append("\n");
                SequenceDescription sd1 = new SequenceDescription();
                sd1.setFrom(finalToOrigin);
                sd1.setTo("数据层");
                sd1.setMessage(subFunctionalProcessing.replace("\\n", ""));
                sd1.setDataGroup(e.getDataGroup());
                sd1.setType(2);
                sequenceDescriptions.add(sd1);
                SequenceDescription sd2 = new SequenceDescription();
                sd2.setFrom("数据层");
                sd2.setTo(finalToOrigin);
                sd2.setMessage("返回数据操作结果");
                sd2.setDataGroup("数据操作结果");
                sd2.setType(2);
                sequenceDescriptions.add(sd2);
            }
        });

        //处理 X
        CosmicExcelModel cosmicExcelModelEnd = list.get(list.size() - 1);
        String subFunctionalProcessing = cosmicExcelModelEnd.getSubFunctionalProcessing();
        subFunctionalProcessing = lineBreak(subFunctionalProcessing, 10);
        sb.append(to).append(" --> ").append(from).append(" : ").append(subFunctionalProcessing).append("\n");

        SequenceDescription sequenceDescriptionEnd = new SequenceDescription();
        sequenceDescriptionEnd.setFrom(toOrigin);
        sequenceDescriptionEnd.setTo(fromOrigin);
        sequenceDescriptionEnd.setDataGroup(cosmicExcelModelEnd.getDataGroup());
        sequenceDescriptionEnd.setMessage(subFunctionalProcessing.replace("\\n", ""));
        sequenceDescriptionEnd.setType(type);
        sequenceDescriptions.add(sequenceDescriptionEnd);

        //结束激活
        participantMap.keySet().forEach(k -> {
            sb.append("deactivate ").append(participantMap.get(k)).append(" #f1f2f6\n");
        });

        //结束
        sb.append("@enduml");

        objects[0] = sb.toString();
        objects[1] = sequenceDescriptions;
        return objects;
    }

    /**
     * 获取输入流
     *
     * @param umlSource uml源码
     * @return 输入流
     */
    public static InputStream getInputStream(String umlSource) {
        SourceStringReader reader = new SourceStringReader(umlSource);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            reader.outputImage(outputStream, new FileFormatOption(FileFormat.PNG));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return new ByteArrayInputStream(outputStream.toByteArray());
    }

    /**
     * 换行
     *
     * @param originalString 原始字符串
     * @param length         换行长度
     * @return 换行后的字符串
     */
    public static String lineBreak(String originalString, int length) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < originalString.length(); i++) {
            sb.append(originalString.charAt(i));
            if ((i + 1) % length == 0) {
                sb.append("\\n");
            }
        }
        return sb.toString();
    }

    public static String getChineseInitials(String chineseString) {
        StringBuilder initials = new StringBuilder();
        for (int i = 0; i < chineseString.length(); i++) {
            char ch = chineseString.charAt(i);
            if (Character.toString(ch).matches("[\\u4E00-\\u9FA5]+")) {
                String pinyin = String.valueOf(PinyinUtil.getFirstLetter(ch));
                initials.append(pinyin);
            } else {
                initials.append(ch);
            }
        }
        return initials.toString();
    }
}
